package com.wutianyi.arithmetic;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.io.ZipOutputStream;
import net.lingala.zip4j.model.ZipModel;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.InternalZipConstants;
import net.lingala.zip4j.util.Zip4jConstants;
import net.lingala.zip4j.zip.ZipEngine;

public class MyZipEngine extends ZipEngine
{

    private ZipModel zipModel;

    private ZipOutputStream zipOutputStream;

    public MyZipEngine(ZipModel zipModel) throws ZipException
    {
        super(zipModel);
        if (zipModel == null)
        {
            throw new ZipException("zip model is null in ZipEngine constructor");
        }
        this.zipModel = zipModel;
    }

    public MyZipEngine(ZipModel zipModel, OutputStream outputStream) throws ZipException
    {
        this(zipModel);
        if(null == outputStream)
        {
            throw new ZipException("Zip outputStream is null");
        }
        zipOutputStream = new ZipOutputStream(outputStream, zipModel);
    }

    
    
    public void addStreamToZips(OutputStream outerOutputStream, InputStream[] inputStreams, String[] names,
            ZipParameters parameters) throws ZipException
    {
        if (inputStreams == null || parameters == null)
        {
            throw new ZipException("one of the input parameters is null, cannot add stream to zip");
        }

        ZipOutputStream outputStream = null;

        try
        {
            checkParameters(parameters);

            outputStream = new ZipOutputStream(outerOutputStream, this.zipModel);

            byte[] readBuff = new byte[InternalZipConstants.BUFF_SIZE];
            int readLen = -1;
            int index = 0;
            for (InputStream inputStream : inputStreams)
            {
                parameters.setFileNameInZip(names[index]);
                outputStream.putNextEntry(null, parameters);

                if (!parameters.getFileNameInZip().endsWith("/") && !parameters.getFileNameInZip().endsWith("\\"))
                {
                    while ((readLen = inputStream.read(readBuff)) != -1)
                    {
                        outputStream.write(readBuff, 0, readLen);
                    }
                }

                outputStream.closeEntry();
                ++index;
            }

            outputStream.finish();

        }
        catch (ZipException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ZipException(e);
        }
        finally
        {
            if (outputStream != null)
            {
                try
                {
                    outputStream.close();
                }
                catch (IOException e)
                {
                    // ignore
                }
            }
        }

    }

    public void addStreamToZip(OutputStream outerOutputStream, InputStream inputStream, ZipParameters parameters)
            throws ZipException
    {
        if (inputStream == null || parameters == null)
        {
            throw new ZipException("one of the input parameters is null, cannot add stream to zip");
        }

        ZipOutputStream outputStream = null;

        try
        {
            checkParameters(parameters);

            outputStream = new ZipOutputStream(outerOutputStream, this.zipModel);

            byte[] readBuff = new byte[InternalZipConstants.BUFF_SIZE];
            int readLen = -1;

            outputStream.putNextEntry(null, parameters);

            if (!parameters.getFileNameInZip().endsWith("/") && !parameters.getFileNameInZip().endsWith("\\"))
            {
                while ((readLen = inputStream.read(readBuff)) != -1)
                {
                    outputStream.write(readBuff, 0, readLen);
                }
            }

            outputStream.closeEntry();
            outputStream.finish();

        }
        catch (ZipException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ZipException(e);
        }
        finally
        {
            if (outputStream != null)
            {
                try
                {
                    outputStream.close();
                }
                catch (IOException e)
                {
                    // ignore
                }
            }
        }

    }

    private void checkParameters(ZipParameters parameters) throws ZipException
    {

        if (parameters == null)
        {
            throw new ZipException("cannot validate zip parameters");
        }

        if ((parameters.getCompressionMethod() != Zip4jConstants.COMP_STORE)
                && parameters.getCompressionMethod() != Zip4jConstants.COMP_DEFLATE)
        {
            throw new ZipException("unsupported compression type");
        }

        if (parameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE)
        {
            if (parameters.getCompressionLevel() < 0 && parameters.getCompressionLevel() > 9)
            {
                throw new ZipException(
                        "invalid compression level. compression level dor deflate should be in the range of 0-9");
            }
        }

        if (parameters.isEncryptFiles())
        {
            if (parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_STANDARD
                    && parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_AES)
            {
                throw new ZipException("unsupported encryption method");
            }

            if (parameters.getPassword() == null || parameters.getPassword().length <= 0)
            {
                throw new ZipException("input password is empty or null");
            }
        }
        else
        {
            parameters.setAesKeyStrength(-1);
            parameters.setEncryptionMethod(-1);
        }

    }
}
